Java内部类使用总结

Categories: Java; Tagged with: ; @ February 13th, 2009 20:44

顶层类只能处于Public跟默认访问级别. 而内部类可以处于任意访问级别.

A 实例内部类:

  • 在创建实例内部类实例时, 外部类的实力必须已经存在.
  • 实例内部类自动持有外部类实例的引用
  • 一个外部类可以有多个内部类 因此不允许外部类直接调用内部类方法或属性. 而内部类进对应一个外部类, 因此可以直接使用外部类的引用.
  • 在实例内部类中不能定义静态成员, 只能定义实例成员.
  • 当内部类与外部类有同名的成员时,如age, 在内部类中直接使用age或this.age表示inner2内age 而OnlyForTest.this.age表示外部类中的age.

B 静态内部类:

  • 静态内部类的实例不会自动持有外部类的特定实例的引用, 因此在创建内部类的实例时, 不必创建外部类的实例.
  • 静态内部类可以直接访问外部类的静态成员, 如果要访问外部类的实例成员, 就必须通过外部类的实例去访问
  • 静态内部类中可以定义静态成员和实例成员
  • 客户类可以通过完整的类名直接访问静态内部类的静态成员.

局部内部类:

  • 与局部变量一样, 不能被访问控制符修饰.
  • 只能在当前方法中使用
  • 不能包含静态成员
  • 在局部类中定义的内部类也不能被访问控制符修饰
  • 局部内部类和实例内部类一样, 可以访问外部类的所有成员, 此外, 局部内部类还可以访问所在方法中的final类型的参数和变量.

匿名类:

  • 匿名类没有构造方法, 但是会调用父类的构造方法.
  • 匿名类尽管没有构造方法, 但是可以在匿名类中提供一段实例初始化代码
  • 除了可以在外部类的方法内定义匿名类之外, 还可以在生命一个成员变量时定义匿名类
  • 匿名类除了可以继承外, 还可以实现接口
  • 匿名类和局部内部类一样, 都可以访问外部类的所有成员, 如果匿名类位于一个方法中, 还可以访问方法内部的final类型的变量和参数

 

举例[实例内部类, 静态内部类]:

 

public class OnlyForTest {
	public static String staticOutMember = "Static Out Member";
	private String userName;
	private int age;
	//省略Getter/Setter
	
	public static void main(String[] args) {
		OnlyForTest oft = new OnlyForTest();
		//A1. 在创建实例内部类实例时, 外部类的实力必须已经存在.
		OnlyForTest.Inner inner = oft.new Inner();
		
		//A3. 一个外部类可以有多个内部类  因此不允许外部类直接调用内部类方法或属性, 在需要时, 使用内部类实例访问内部类成员
		//而内部类进对应一个外部类, 因此可以直接使用外部类的引用.
		oft.age = oft.new Inner2().age;
		
		inner.setOutInstanceNmae("tom");
		
		oft.new Inner2().print();
		
		//B1: 直接访问静态类静态成员, 不需创建实例.
		System.out.println(staticInnerClass.staticInnerMember);
		
		new staticInnerClass().printNmae();
	}
	
	class Inner {
		public void setOutInstanceNmae(String s){
			//A2. 实例内部类自动持有外部类实例的引用
			userName = s;
			System.out.println("内部类直接引用外部类实例 - 内部类赋值外部类成员 " + userName);
		}
	}//end of class Inner
	
	class Inner2 {
		int age= 2;
		//A4. 在实例内部类中不能定义静态成员, 只能定义实例成员.
		//报错!static int staticInt = 4;
		public void print() {
			System.out.println(userName);
			//A5. 当内部类与外部类有同名的成员时,如age
			//直接使用age或shis.age表示inner2内age
			//OnlyForTest.this.age表示外部类中的age
			System.out.println(age);
			System.out.println(OnlyForTest.this.age);
			//B2. 直接访问外部类的静态成员
			System.out.println(staticOutMember);
		}
	}//end of class Inner2
	
	static class staticInnerClass {
		public static String staticInnerMember = "Static Inner Member";
		public void printNmae() {
			//B3:静态内部类在访问外部类实例变量时, 必须通过外部实例
			OnlyForTest o = new OnlyForTest();
			o.userName = "set by Static Inner Class";
			System.out.println(o.userName);
		}
	}//end of staticInnerClass
}

编译后的Class文件:

image

DeCompiler之后 Inner的构造函数:

    OnlyForTest$Inner2()
    {
        this$0 = OnlyForTest.this;
        super();
        age = 2;
    }

可见, 每个inner实例都会自动包含一个外部类OnlyForTest的引用

而staticInnerClass类的构造函数为空, 因此他的实例不会自动引用外部类

从Decompile的Class文件来看, 对JVM来说, 无所谓内外类之分, 他们都是Class, 只是内部类会自动引用外部类实例 或是 其他特定的功能而已.

Servlet实践 2-2

Categories: Java; Tagged with: ; @ February 13th, 2009 20:24

Login设计

当Request进入时, 先通过request.getSession(false)检查Session是否存在, 如果存在则表明已经登录,则通过sendRedirect(Calc.URL_MAIN)跳转到主页面
Session不存在时, 通过request.getParameter()方法获得输入的用于名及密码, 通过ServletContext的get Parameter()方法获得用户名及密码, 如果相互都equals, 则增加Session, 登录成功, 转入主页面. 如果不equals, 则提示输入有误, 重新输入
重要方法:

response.sendRedirect();
request.getParameter();
getServletContext().getAttribute();

package com.insprise.servletStu;
//省略import
public class Login extends HttpServlet {
      public static final String USER_NAME = "userName";
	public static final String PASS_WORD = "passWord";
	private static final long serialVersionUID = 1L;
	private static final String INPUT_USER_NAME = "inputUserName";
	private static final String INPUT_PASSWORD = "inputPassWord";
	private String passWord;
      private String name;

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		name = (String) getServletContext().getAttribute(USER_NAME);
		passWord = (String) getServletContext().getAttribute(PASS_WORD);
		HttpSession session = request.getSession(false);
		if (session != null) {
			response.sendRedirect(Calc.URL_MAIN);//如果已经登录后再次打开登录页面, 跳转到主界面
			return;
		} else {
			response.setContentType("text/html");
			response.setCharacterEncoding("utf-8");
			PrintWriter out = response.getWriter();

			if (request.getParameter(INPUT_USER_NAME) != null) {
				String inputUserName = request.getParameter(INPUT_USER_NAME);
				String inputPassWord = request.getParameter(INPUT_PASSWORD);
				if (inputUserName.equals(USER_NAME) && inputPassWord.equals(passWord)) {
					request.getSession().setAttribute(Calc.CURRENT_USER, USER_NAME);
					response.sendRedirect(Calc.URL_MAIN);
					return;
				} else {
					//登录失败
				}
			} else {
				//省略若干println…
	}
	//省略doPost
}

 

 

CalcListener实现

该类实现ServletContextListener接口, 在Application初始化时, contextInitialized(ServletContextEvent sce)方法会被自动呼叫, 在此之后, Filter与Servlet开始初始化.

使用getServletContext().getInitParameter()方法获得web.xml中<context-param>标签中的内容.

使用ServletContext的setAttribute方法为获得用户名及密码, 并作为Attribute加入到ServletContext中

重要方法:

sce.getServletContext();

ServletContext.getInitParameter();

ServletContext. setAttribute();

public class CalcListener implements ServletContextListener {

private static Logger log = Logger.getLogger(CalcListener.class); 

public void contextInitialized(ServletContextEvent sce) {

log.info("Context Started!" + sce.getServletContext());

ServletContext context = sce.getServletContext();

Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION));

Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION));

//读取到用户名及密码, 加入到Context Attribute中

context.setAttribute(Login.USER_NAME, new String(context.getInitParameter(Login.USER_NAME)));

context.setAttribute(Login.PASS_WORD, new String(context.getInitParameter(Login.PASS_WORD)));
}

}

 

2.4 抓图

clip_image002 clip_image004

clip_image006

Servlet实践 2-1

Categories: Java; Tagged with: ; @ February 13th, 2009 20:21

实践

2.1 需求及用例

创建一个简单的计算器, 当用户输入整数时, 输出其平方值. 但用户需要登录才能进行操作, 用户的登录信息放在web.xml中, 每次登录用户可以使用该计算器三次, 三次使用结束后, 提示用户推出后重新登录继续使用.

User Case 用例

登录系统

Brief description

登录系统

Scope/Level

AthenaES/User goal

Primary actor/role

Root

Minimal & Success Guarantees 保证

Minimal: The system logs how far it may get
Success:登录成功

Preconditions 前提

Triggers 引发条件

用户打开登录界面

Main Success Scenario 成功场景

1. 系统提示用户输入用户名及密码
2. 用户输入用户名及密码
3. 系统核对用户名及密码
4. 系统提示用户登录成功

Extension 扩展 #1

3a: 系统无法核实用户身份, 提示其用户名或密码错误, 并提示用户重新输入

Notes and Issues

User Case 用例

计算平方

Brief description

计算平方

Scope/Level

AthenaES/User goal

Primary actor/role

Root

Minimal & Success Guarantees 保证

Minimal: The system logs how far it may get
Success: 成功显示结果

Preconditions 前提

Actor is logged in

Triggers 引发条件

用户点击”计算”

Main Success Scenario 成功场景

1. 确认用户使用次数未达到上限
2. 用户输入整数
3. 用户点击”计算”
4. 系统确认输入合法
5. 系统计算平方值, 系统将用户使用次数加一, 系统显示结果, 提示用户使用次数.
用户重复步骤1~5 , 直到用户关闭程序或使用次数达到上限

Extension 扩展 #1

1a: 用户使用次数达到上限, 系统提示用户重新登陆后继续使用

Extension 扩展 #2

4a: 系统检测到用户输入不合法, 系统提示用户重新输入 用户重新进行2~4

Notes and Issues

2. 2 数据分析:


1. ServltContext
层次的数据: 由于用户名及最大限制次数等信息在整个Servlet进程中都会被使用, 因此使用Context的Listener来实现.

2. Session层次的数据: 用户本次登录已使用次数应记录在Seesion中, 在当前Session中使用

3. Request层次的数据: 用户输入的数值尽在当前Request中使用

2. 3 具体实现

</>

Calc主页面实现:

首先检查Session是否存在, 不存在则跳转到登录界面.
Session存在, 则通过session.getAttribute()获得该用户在当前Session中的使用次数, 如果为Null, 则设置为零, setAttribute.
如果不为Null, 则判断是否小于MAX_CALCULATION_TIMES_PER_SESSION, 如果大于三次则提示其退出登录重新登录继续使用.
小于MAX_CALCULATION_TIMES_PER_SESSION则呼叫proccessRequet()方法输出计算器. 该方法将会调用calculator()方法, 该方法将返回输入值的平方,同时将Session中的使用次数加一.
重要方法:
request.getSession(false);//获得Session, 如果不存在, 则返回null
session.getAttribute(ACCESS_COUNT);
session.setAttribute(ACCESS_COUNT, accessCount);
request.getSession().invalidate(); //
清空session

package com.insprise.servletStu;
//省略import
public class Calc extends HttpServlet {
	public static final String MAX_CALCULATIONAS_PER_SESSION = “maxNumber”; // 最大使用次数的Key
	public static int MAX_CALCULATION_TIMES_PER_SESSION; // 最大使用次数
	public static final String CURRENT_USER = “currentUser”; // 当前用户
	public static final String URL_MAIN = “./Calc”; // 主页面
	public static final String URL_LOGIN = “./Login”; // 登录页面
	public static final String URL_LOGOUT = “./Logout”; // 注销页面
	private static final String ACCESS_COUNT = “accessCount”; // 使用次数
	private static final String INPUT_INT = “inputInt”;
	private static Logger log = Logger.getLogger(Calc.class);
	private static final int MAX_INT = 46340; // Math.sqrt(Integer.MAX_VALUE)

	private HttpSession session;
	private Integer accessCount; // 使用次数

	public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		session = request.getSession(false);
		if (validatUser(session)) { // 已登录
			accessCount = (Integer) session.getAttribute(ACCESS_COUNT);
			if (accessCount == null) { // 如果Session中不存在accessCount, 则表明其为新登录用户,
				// 赋值为0;
				accessCount = 0;
				session.setAttribute(ACCESS_COUNT, accessCount);
			}
			if (checkUseTimes(accessCount)) { // 已登录, 且使用次数在限制之内, 进行操作.
				proccessRequet(request, response);

			} else { // 已登录, 但使用次数操过限制,提示用户重新登陆.
				accessOverTime(request, response);
			}
		} else { // 未登录
			response.sendRedirect(URL_LOGIN);
		}
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
		doGet(request, response);
	}

	// 检查用户是否已登录
	private boolean validatUser(HttpSession s) {
		if (session != null) {
			return true;
		}
		return false;
	}

	// 检查用户使用次数是否超过限制
	private boolean checkUseTimes(Integer number) {
		//省略
	}

	// 使用次数超过限制之后 提示其登录;
	private void accessOverTime(HttpServletRequest request, HttpServletResponse response) throws IOException {
		request.getSession().invalidate();
		response.getWriter().println("本次登录已使用完毕, 请重新登录");
		return;
	}

	// 获得输入数值, 计算结果, 返回信息
	private void proccessRequet(HttpServletRequest request, HttpServletResponse response) throws IOException {
		Integer i;
		String title = "求整数平方";

		response.setContentType("text/html");
		response.setCharacterEncoding("utf-8");
		// 不使用缓存 在点击推出后 按后退按钮时 不会出现缓存中的内容.
		response.setHeader("Cache-Control", "no-store");
		response.setDateHeader("Expires", 0);
		response.setHeader("Pragma", "no-cache");

		PrintWriter out = response.getWriter();
		out.println(ServletUtilities.headwithTitle(title));
		out.println("

" + title + "


"); out.println("

当前用户: " + session.getAttribute(CURRENT_USER) + " [退出]

"); String numberString = request.getParameter(INPUT_INT); if (numberString != null) { try { out.println("

" + i + " 的平方是: " + calculator(i) + "

"); log.info("累计第[" + accessCount + "]次使用, 本次计算的参数为: " + i); } catch (Exception e) { out.println("您可能输入的不是整数 或您输入的整数绝对值过大

请输入: 一个绝对值小于" + MAX_INT + " 的整数

"); } } out.println("

您总共可以使用 " + MAX_CALCULATION_TIMES_PER_SESSION + "次, 您已累计使用:" + accessCount + "次
"); // 最后一次运行时, 停止输出Form; if (accessCount.equals(MAX_CALCULATION_TIMES_PER_SESSION)) { out.println("已达到最大使用次数限制, 请退出后重新登陆"); return; } printForm(out); out.println(""); } // 计算 private int calculator(int i) { accessCount = new Integer(accessCount.intValue() + 1); // 使用次数增加一次 session.setAttribute("accessCount", accessCount); return i * i; } // 打印一个Form private void printForm(PrintWriter out) { out.print("


"); out.println("请输入一个整数: "); out.println("
"); out.println(" "); } }

Servlet学习总结 – Servlet Reference

Categories: Java; Tagged with: ; @ February 13th, 2009 20:12

2009-2-13 14-56-18

很简单的几个概念, 其他还是OO.

A: Request常用方法

request中获取参数

方法

说明

getParameter(String name);

获得request中参数名name对应的值, 如果不存在该参数, 则返回Null – 注意: 名称大小写敏感

GetParameterNames

返回一个枚举对象, 包含了所在request中的所有参数名, 如果request没有参数, 该方法返回一个空的枚举

GetParameterValues(String name)

返回一个Sting[], 包含参数所对应的所有的值, 如果参数不存在,则返回Null.

getParameterMap

返回一个Map对象, 以参数名称为Key(String), 参数对应值为Value(类型String[])

getSession()

获得当前Session

Response中输出信息

方法

说明

setContentType(String type)

声明返回数据的MIME类型

setCharacterEncoding(String charset)

声明Response的编码

getWriter();

返回一个PrintWriter, 可通过该对象的print 或printlin方法向客户端发送字符串数据流

sendRedirect(String location)

设置转向 – 转到某地址

B: Session常用方法:

方法

说明

getServletContext()

获得Session所在的ServletContext对象

getAttribute(String name)

返回该Session中name对应的Attribute对象, 如果没有则返回null

getAttributeNames()

返回一个枚举, 包括Seesion中所有的Attribut 对应的name,

setAttribute(String name, Object value)

在Session中加入Attribute

invalidate()

销毁Session

C: ServeletContext常用方法

方法

说明

getInitParameter(String name)

返回一个String, 包含对应的初始化参数, 如果不存在则返回null, init参数可在web.xml中使用<context-param>设定

getInitParameterNames()

返回由所有的init参数名称组成的枚举, 如果不存在任何init参数, 则返回空的枚举

getAttribute(String name)

返回运行时设定相应的Attribute 对象, 如不存在则返回null

getAttributeNames()

返回一个枚举, 包含所有Attribute的name, 如果不存在任何Attribue, 则返回一个空的枚举

setAttribute(String name, Object object)

在运行时增加Attribute

removeAttribute(String name)

删除对应name的Attribute

D: Servlet框架理解

Servlet通过IoC(Inversion of Control意为控制反转)将代码交给框架来执行. 因此只需要在Servlet类中扩展HttpServlet, 覆盖doGet, doPost等方法, 就可成功创建并执行一个Servlet. 在有客户Request时, 将由Service方法分析Request, 并交由对应方法处理, 如Get交由doGet方法处理.

Listener: Servlet中的listener类似于AS中的Event kind, 在Listener支持的事件 – 如Servlet初始化完毕, Session初始化, Request Destory,等, 在这些事件发生后, 将自动运行相应的方法(IoC)

如果需要Listener或Filter, 则在编写好相应的类之后, 加入到web.xml描述文件中.

Servlet 的数据层次:
1. ServletContext: Application初始化时会呼叫Listener contextInitialized(), 可以在该方法中获得或设定ServletContext生命周期内的数据. 如可以通过getInitParameter来获取web.xml中定义好的初始化参数, 也可以通过setAttribute方法设定Attribute, 这些Attribute将在整个Servlet生命周期内有效.

2. HttpSession: 当获得Session之后, 可以进行setAttribute等方法设定Session’的Attribute – 在该Session中有效

3. HttpRequest: 在doGet, doPost等方法中, 可以通过request. get Parameter(String name)等方法来获得request中的数据, 这些数据仅对当前的这个Request有效

web.xml中<context-param>与<init-param>的差别及各自的读取方式

Categories: Java; Tagged with: ; @ February 13th, 2009 19:40

事由: 最近几天看了点Servlet, 打算写一个小程序, 需要在application启动时从web.xml中加载一个参数, 由于仅在Login这个Servlet中使用, 于是我是这样实现的:

  
    
    Login
    Login
    com.insprise.servletstu.Login
    
      maxNumber
      maxNumber
      3
    
  
  
    Login
    /Login
  
  

然后在Login中重写init()方法, 如下:

Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(this.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION));

功能以实现, 将最大值从web.xml中读取出来, 赋值给一个Global的变量.

===============================承上启下的分割线==============================================

但随着学习的深入, 发现容器并不一定会保持Servlet一直存活, 也许会在Request介绍后Destory掉Login Servlet, 然后如果再次Request Login, 则会重新init(), 于是重新读取web.xml, 重新赋值–明显第一次有效的, 后面的都是无用功.

从逻辑上分析, 该最大值应属于ServletContext级别的数据, 于是应使用Listener, 监听Application初始化, 在Application初始化时读取.

于是我没有改动web.xml, 设置了监听函数:

    public void contextInitialized(ServletContextEvent sce) {
        log.info("Context Started!" + sce.getServletContext());
        ServletContext context = sce.getServletContext();
        Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION));
    }

但不论如何都会有NullPoint的错误. 于是意识到该应在web.xml使用<context-param>设定参数, 而非某个<Servlet>下的<init-param>标签. 修改后的web.xml:

 
           maxNumber
           3

=====================================总结性的分割线============================================

总而言之:

使用ServletContext.getInitParameter()方法 可以获得web.xml中的<context-param>参数 – 使用范围很广, 能getServletContext就可以使用.

在Servlet的init()函数中使用this.getInitParameter()获得web.xml的<init-param>参数 – 较为局限的参数. 注意逻辑上的层次划分

Newer Posts <-> Older Posts



// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.